Skip to content

fix(dynamic-agents): correlate subagent namespaces across langgraph tasks-chunk shapes#1683

Merged
subbaksh merged 1 commit into
prebuild/hotfix-cnoe-agent-utils-anthropic-bedrockfrom
prebuild/fix-subagent-namespace-correlation
Jun 4, 2026
Merged

fix(dynamic-agents): correlate subagent namespaces across langgraph tasks-chunk shapes#1683
subbaksh merged 1 commit into
prebuild/hotfix-cnoe-agent-utils-anthropic-bedrockfrom
prebuild/fix-subagent-namespace-correlation

Conversation

@cisco-erilutz
Copy link
Copy Markdown
Collaborator

Summary

Fixes a regression where subagent narration leaks into the parent agent's
final response
(e.g. a streamed answer that begins with "I'll look up the
weather...", "I'll search for...", "Now let me..." before the real answer).

The leak appears after upgrading langgraph to >= 1.2 and is not caused
by the LLM client choice (ChatBedrockConverse vs ChatAnthropicBedrock) —
both stream identically. The trigger is a change in the shape of langgraph's
tasks-mode stream metadata that silently broke subagent namespace
correlation in the AG-UI encoder.

Root cause

AGUIStreamEncoder (via LangGraphStreamHelper._handle_tasks_chunk) builds a
{namespace -> tool_call_id} mapping from tasks-mode chunks so that subagent
events can be attributed to the task tool call that spawned them. Clients use
this attribution (emitted as NAMESPACE_CONTEXT events) to tell subagent output
apart from the parent agent's own response.

langgraph changed the shape of the tasks chunk input field between versions:

# langgraph < 1.2
input = {
    "__type": "tool_call_with_context",
    "tool_call": {"name": "task", "id": "toolu_...", "type": "tool_call", ...},
    "state": {...},
}

# langgraph >= 1.2
input = [
    {"name": "task", "id": "toolu_...", "type": "tool_call", ...}
]

_handle_tasks_chunk only read the dict shape (input.get("tool_call")). On
langgraph >= 1.2, input is a list, so the lookup returned {}, the mapping
stayed empty, and correlate_namespace took its fallback branch:

# Unknown namespace — treat as parent agent
return ()

Every subagent namespace was therefore classified as the parent. As a result:

  1. No NAMESPACE_CONTEXT events were emitted (the namespace never "changed"
    from the parent's empty tuple).
  2. Consumers that suppress subagent text based on namespace had nothing to key
    on, so subagent narration was rendered as the parent's answer.

This also produced a flood of [sse:correlate] Unknown namespace ... warnings
in the logs, which is the visible fingerprint of the bug.

Fix

Parse both input shapes via a new _extract_task_tool_calls helper and
iterate the resulting tool calls. The dict (tool_call_with_context) and list
forms both normalize to a list of tool-call dicts, so the mapping populates
correctly regardless of langgraph version. No protocol or consumer changes are
required — restoring the mapping makes the existing NAMESPACE_CONTEXT
attribution work again for all clients.

How it was verified

Reproduced end-to-end against a parent agent invoking parallel subagents,
streaming through the real AGUIStreamEncoder, comparing langgraph 1.0.x vs
1.2.x:

langgraph 1.0.x langgraph 1.2.x (before fix) langgraph 1.2.x (after fix)
NAMESPACE_CONTEXT events present 0 present
"Unknown namespace" warnings 0 many 0
Subagent narration in answer suppressed leaked suppressed

Tests

Adds TestTasksChunkNamespaceMapping covering:

  • legacy dict shape populates the mapping
  • list shape populates the mapping
  • end-to-end correlation after a list-shape chunk
  • the uncorrelated-fallback failure mode (guard)
  • non-task tool calls are ignored
  • result chunks without a tool call are ignored

All encoder/namespace tests pass; ruff clean.

Type of Change

  • Bugfix

…asks-chunk shapes

[GAI]

The AG-UI stream encoder builds a {namespace -> tool_call_id} mapping from
`tasks`-mode stream metadata so subagent events can be attributed to the
`task` tool call that spawned them. Clients rely on this attribution
(NAMESPACE_CONTEXT events) to distinguish subagent output from the parent
agent's response.

langgraph changed the shape of the `tasks`-mode `input` field between
versions:
  - < 1.2: input is a dict {"__type": "tool_call_with_context",
    "tool_call": {...}, "state": {...}}
  - >= 1.2: input is a bare list of tool_call dicts

`_handle_tasks_chunk` only parsed the dict shape (input.tool_call), so on
langgraph >= 1.2 the mapping stayed empty. With no mapping, every subagent
namespace fell through `correlate_namespace`'s 'unknown -> treat as parent'
path, no NAMESPACE_CONTEXT events were emitted, and subagent narration leaked
into the parent agent's final response.

Parse both shapes via a new `_extract_task_tool_calls` helper and iterate
the resulting tool_calls. Add regression tests covering both chunk shapes,
end-to-end correlation, the uncorrelated fallback, and non-task tool calls.

Signed-off-by: Erik Lutz <elutz@splunk.com>
@caipe-ci-build
Copy link
Copy Markdown

caipe-ci-build Bot commented Jun 1, 2026

Prebuild Artifacts for d340054

Branch: prebuild/fix-subagent-namespace-correlation
Commit: d340054

Docker Images

Artifact Image Tag Status CI
caipe-dynamic-agents ghcr.io/cnoe-io/prebuild/caipe-dynamic-agents fix-subagent-namespace-correlation-2 Published CI
Docker pull commands
docker pull ghcr.io/cnoe-io/prebuild/caipe-dynamic-agents:fix-subagent-namespace-correlation-2

These prebuild artifacts will be automatically cleaned up when the PR is closed or merged.

@subbaksh subbaksh marked this pull request as ready for review June 4, 2026 10:28
@subbaksh subbaksh requested a review from sriaradhyula as a code owner June 4, 2026 10:28
@subbaksh subbaksh merged commit 93652c4 into prebuild/hotfix-cnoe-agent-utils-anthropic-bedrock Jun 4, 2026
10 checks passed
@subbaksh subbaksh deleted the prebuild/fix-subagent-namespace-correlation branch June 4, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants